---
title: Dynamic Type System Comparison
description: In-depth understanding of the differences between Python and JavaScript dynamic type systems, mastering type conversion rules, truthiness evaluation, and type checking methods.
---

# Dynamic Type System Comparison

Both Python and JavaScript are dynamically typed languages, but they have significant differences in type handling. Python is a strongly-typed dynamic language, while JavaScript is a weakly-typed dynamic language. This module will help you understand these differences and avoid common type-related pitfalls.

## Fundamental Type System Concepts

### Strong Typing vs Weak Typing

<PythonEditor title="Type System Differences" compare={true}>
```python !! py
# Python: Strongly-typed dynamic language
# No implicit type conversion, strict type checking

# Basic types
number = 42
text = "Hello"
flag = True
empty = None

print(f"Number type: {type(number)}")
print(f"String type: {type(text)}")
print(f"Boolean type: {type(flag)}")
print(f"Null type: {type(empty)}")

# Strict type checking
try:
    result = "5" + 3  # Error: cannot add string and number
except TypeError as e:
    print(f"Type error: {e}")

try:
    result = "5" * 3  # Allowed: string repetition
    print(f"String repetition: {result}")
except TypeError as e:
    print(f"Error: {e}")

# Explicit type conversion
num_str = "123"
num_int = int(num_str)      # Explicit conversion to integer
num_float = float(num_str)  # Explicit conversion to float
bool_val = bool(num_str)    # Explicit conversion to boolean

print(f"Conversion results: {num_int}, {num_float}, {bool_val}")

# Type checking functions
print(f"Is integer: {isinstance(number, int)}")
print(f"Is string: {isinstance(text, str)}")
print(f"Type name: {type(number).__name__}")
```

```javascript !! js
// JavaScript: Weakly-typed dynamic language
// Allows implicit type conversion (type coercion)

// Basic types
const number = 42;
const text = "Hello";
const flag = true;
const empty = null;
let undefinedVal;

console.log(`Number type: ${typeof number}`);
console.log(`String type: ${typeof text}`);
console.log(`Boolean type: ${typeof flag}`);
console.log(`Null type: ${typeof empty}`);  // Returns "object" (famous JS quirk)
console.log(`Undefined type: ${typeof undefinedVal}`);

// Automatic type coercion
const result1 = "5" + 3;    // "53" (number converted to string)
const result2 = "5" - 3;    // 2 (string converted to number)
const result3 = "5" * 3;    // 15 (string converted to number)

console.log(`Concatenation: ${result1}`);
console.log(`Subtraction: ${result2}`);
console.log(`Multiplication: ${result3}`);

// Explicit type conversion
const numStr = "123";
const numInt = parseInt(numStr);       // Explicit conversion to integer
const numFloat = parseFloat(numStr);   // Explicit conversion to float
const boolVal = Boolean(numStr);       // Explicit conversion to boolean

console.log(`Conversion results: ${numInt}, ${numFloat}, ${boolVal}`);

// Type checking functions
console.log(`Is number: ${typeof number === 'number'}`);
console.log(`Is string: ${typeof text === 'string'}`);
console.log(`Is array: ${Array.isArray([])}`);
console.log(`Constructor: ${number.constructor.name}`);
```
</PythonEditor>

### Type Coercion Rules

<PythonEditor title="Type Conversion Rules" compare={true}>
```python !! py
# Python: Explicit type conversion required

# Number to string conversion
num = 42
str_num = str(num)  # Must be explicit
print(f"Number to string: '{str_num}'")

# String to number conversion
str_val = "123"
try:
    int_val = int(str_val)
    float_val = float(str_val)
    print(f"String to numbers: {int_val}, {float_val}")
except ValueError as e:
    print(f"Conversion error: {e}")

# Invalid conversions raise exceptions
try:
    invalid = int("abc")
except ValueError as e:
    print(f"Invalid conversion: {e}")

# Boolean conversions
print(f"bool(''): {bool('')}")        # False
print(f"bool('hello'): {bool('hello')}")  # True
print(f"bool(0): {bool(0)}")          # False
print(f"bool(42): {bool(42)}")        # True
print(f"bool([]): {bool([])}")        # False
print(f"bool([1]): {bool([1])}")      # True

# Comparison without conversion
print(f"42 == '42': {42 == '42'}")    # False (different types)
print(f"42 == 42: {42 == 42}")        # True

# List concatenation requires same types
list1 = [1, 2, 3]
list2 = [4, 5, 6]
combined = list1 + list2  # Works: same type
print(f"List concatenation: {combined}")

# This would fail:
# mixed = [1, 2] + "hello"  # TypeError
```

```javascript !! js
// JavaScript: Automatic type coercion

// Number to string conversion (automatic)
const num = 42;
const strNum = num + "";  // Automatic conversion
const explicitStr = String(num);  // Explicit conversion
console.log(`Number to string: '${strNum}', '${explicitStr}'`);

// String to number conversion
const strVal = "123";
const intVal1 = +strVal;           // Unary + operator
const intVal2 = Number(strVal);    // Number constructor
const intVal3 = parseInt(strVal);  // parseInt function
const floatVal = parseFloat(strVal);

console.log(`String to numbers: ${intVal1}, ${intVal2}, ${intVal3}, ${floatVal}`);

// Invalid conversions return NaN
const invalid = Number("abc");
console.log(`Invalid conversion: ${invalid}`);  // NaN
console.log(`Is NaN: ${isNaN(invalid)}`);       // true

// Boolean conversions (automatic in conditions)
console.log(`Boolean(''): ${Boolean('')}`);        // false
console.log(`Boolean('hello'): ${Boolean('hello')}`);  // true
console.log(`Boolean(0): ${Boolean(0)}`);          // false
console.log(`Boolean(42): ${Boolean(42)}`);        // true
console.log(`Boolean([]): ${Boolean([])}`);        // true (arrays are always truthy)
console.log(`Boolean(null): ${Boolean(null)}`);    // false

// Comparison with type coercion
console.log(`42 == '42': ${42 == '42'}`);    // true (coerced)
console.log(`42 === '42': ${42 === '42'}`);  // false (strict)

// Array concatenation with coercion
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = arr1.concat(arr2);  // Works: same type
console.log(`Array concatenation: ${combined}`);

// Mixed type concatenation (becomes strings)
const mixed = [1, 2] + "hello";  // "1,2hello"
console.log(`Mixed concatenation: ${mixed}`);
```
</PythonEditor>

## Truthiness and Falsy Values

### Falsy Values Comparison

<PythonEditor title="Falsy Values" compare={true}>
```python !! py
# Python falsy values
falsy_values = [
    False,      # Boolean false
    None,       # Null value
    0,          # Zero (integer)
    0.0,        # Zero (float)
    0j,         # Zero (complex)
    "",         # Empty string
    [],         # Empty list
    (),         # Empty tuple
    {},         # Empty dictionary
    set(),      # Empty set
    frozenset() # Empty frozen set
]

print("Python falsy values:")
for value in falsy_values:
    print(f"{repr(value)}: {bool(value)}")

# All other values are truthy
truthy_values = [
    True,
    1,
    -1,
    0.1,
    "hello",
    " ",        # Space is truthy
    [0],        # List with zero is truthy
    {"key": None}  # Dict with key is truthy
]

print("\nPython truthy values:")
for value in truthy_values:
    print(f"{repr(value)}: {bool(value)}")

# Conditional usage
def check_value(val):
    if val:
        return f"{repr(val)} is truthy"
    else:
        return f"{repr(val)} is falsy"

test_values = [0, "", [], None, "hello", [1]]
for val in test_values:
    print(check_value(val))
```

```javascript !! js
// JavaScript falsy values
const falsyValues = [
    false,      // Boolean false
    null,       // Null value
    undefined,  // Undefined value
    0,          // Zero
    -0,         // Negative zero
    0n,         // BigInt zero
    NaN,        // Not a Number
    "",         // Empty string
    '',         // Empty string (single quotes)
    ``          // Empty template literal
];

console.log("JavaScript falsy values:");
falsyValues.forEach(value => {
    console.log(`${JSON.stringify(value)}: ${Boolean(value)}`);
});

// All other values are truthy (including empty arrays and objects!)
const truthyValues = [
    true,
    1,
    -1,
    0.1,
    "hello",
    " ",        // Space is truthy
    [],         // Empty array is truthy!
    {},         // Empty object is truthy!
    [0],        // Array with zero is truthy
    {key: null} // Object with key is truthy
];

console.log("\nJavaScript truthy values:");
truthyValues.forEach(value => {
    console.log(`${JSON.stringify(value)}: ${Boolean(value)}`);
});

// Conditional usage
function checkValue(val) {
    if (val) {
        return `${JSON.stringify(val)} is truthy`;
    } else {
        return `${JSON.stringify(val)} is falsy`;
    }
}

const testValues = [0, "", [], null, undefined, "hello", [1], {}];
testValues.forEach(val => {
    console.log(checkValue(val));
});

// Special cases with arrays and objects
console.log("\nSpecial array/object cases:");
console.log(`[] == false: ${[] == false}`);    // true (coerced)
console.log(`[] === false: ${[] === false}`);  // false (strict)
console.log(`[].length: ${[].length}`);        // 0
console.log(`!![] : ${!![]}`);                 // true (array is truthy)
```
</PythonEditor>

### Conditional Logic Differences

<PythonEditor title="Conditional Logic" compare={true}>
```python !! py
# Python conditional patterns

# Checking for empty collections
def process_data(data_list):
    if not data_list:  # Checks for empty list
        return "No data to process"
    
    # Process non-empty list
    return f"Processing {len(data_list)} items"

# Testing with different values
test_cases = [[], [1, 2, 3], None]
for case in test_cases:
    try:
        result = process_data(case)
        print(f"Input: {case} -> {result}")
    except TypeError as e:
        print(f"Error with {case}: {e}")

# Checking for None vs empty
def safe_operation(value):
    if value is None:
        return "Value is None"
    elif not value:
        return "Value is empty but not None"
    else:
        return f"Value has content: {value}"

test_values = [None, "", [], 0, "hello"]
for val in test_values:
    print(f"{repr(val)}: {safe_operation(val)}")

# Dictionary key checking
user_data = {"name": "Alice", "age": None, "email": ""}

if "name" in user_data and user_data["name"]:
    print(f"User name: {user_data['name']}")

if "age" in user_data and user_data["age"] is not None:
    print(f"User age: {user_data['age']}")
else:
    print("Age not provided")

# Default value patterns
def get_setting(config, key, default=None):
    return config.get(key) or default

config = {"debug": False, "timeout": 0, "name": ""}
print(f"Debug: {get_setting(config, 'debug', True)}")
print(f"Timeout: {get_setting(config, 'timeout', 30)}")
print(f"Name: {get_setting(config, 'name', 'Default')}")
```

```javascript !! js
// JavaScript conditional patterns

// Checking for empty collections
function processData(dataList) {
    // Must explicitly check for null/undefined and length
    if (!dataList || dataList.length === 0) {
        return "No data to process";
    }
    
    return `Processing ${dataList.length} items`;
}

// Testing with different values
const testCases = [[], [1, 2, 3], null, undefined];
testCases.forEach(testCase => {
    try {
        const result = processData(testCase);
        console.log(`Input: ${JSON.stringify(testCase)} -> ${result}`);
    } catch (error) {
        console.log(`Error with ${testCase}: ${error.message}`);
    }
});

// Checking for null/undefined vs empty
function safeOperation(value) {
    if (value === null) {
        return "Value is null";
    } else if (value === undefined) {
        return "Value is undefined";
    } else if (!value) {
        return "Value is falsy but not null/undefined";
    } else {
        return `Value has content: ${value}`;
    }
}

const testValues = [null, undefined, "", [], 0, "hello"];
testValues.forEach(val => {
    console.log(`${JSON.stringify(val)}: ${safeOperation(val)}`);
});

// Object property checking
const userData = {name: "Alice", age: null, email: ""};

if (userData.name && userData.name.length > 0) {
    console.log(`User name: ${userData.name}`);
}

if (userData.hasOwnProperty('age') && userData.age !== null) {
    console.log(`User age: ${userData.age}`);
} else {
    console.log("Age not provided");
}

// Default value patterns (multiple approaches)
function getSetting(config, key, defaultValue = null) {
    // Approach 1: Using logical OR (beware of falsy values)
    return config[key] || defaultValue;
}

function getSettingSafe(config, key, defaultValue = null) {
    // Approach 2: Nullish coalescing operator (ES2020)
    return config[key] ?? defaultValue;
}

const config = {debug: false, timeout: 0, name: ""};
console.log(`Debug (OR): ${getSetting(config, 'debug', true)}`);      // true (wrong!)
console.log(`Debug (??): ${getSettingSafe(config, 'debug', true)}`);  // false (correct)
console.log(`Timeout (OR): ${getSetting(config, 'timeout', 30)}`);    // 30 (wrong!)
console.log(`Timeout (??): ${getSettingSafe(config, 'timeout', 30)}`); // 0 (correct)
console.log(`Name (OR): ${getSetting(config, 'name', 'Default')}`);   // "Default"
console.log(`Name (??): ${getSettingSafe(config, 'name', 'Default')}`); // ""
```
</PythonEditor>

## Type Checking and Validation

### Runtime Type Checking

<PythonEditor title="Type Checking Methods" compare={true}>
```python !! py
# Python type checking

import types
from typing import Union, List, Dict, Any

def comprehensive_type_check(value):
    """Comprehensive type checking in Python"""
    
    # Basic type checking
    print(f"Value: {repr(value)}")
    print(f"Type: {type(value)}")
    print(f"Type name: {type(value).__name__}")
    
    # isinstance checks
    print(f"Is int: {isinstance(value, int)}")
    print(f"Is str: {isinstance(value, str)}")
    print(f"Is list: {isinstance(value, list)}")
    print(f"Is dict: {isinstance(value, dict)}")
    
    # Multiple type checking
    print(f"Is number: {isinstance(value, (int, float))}")
    print(f"Is iterable: {hasattr(value, '__iter__')}")
    print(f"Is callable: {callable(value)}")
    
    # Duck typing checks
    print(f"Has len: {hasattr(value, '__len__')}")
    print(f"Has keys: {hasattr(value, 'keys')}")
    print(f"Has append: {hasattr(value, 'append')}")
    
    print("-" * 40)

# Test different types
test_values = [
    42,
    "hello",
    [1, 2, 3],
    {"key": "value"},
    None,
    lambda x: x,
    set([1, 2, 3])
]

for val in test_values:
    comprehensive_type_check(val)

# Type validation function
def validate_user_data(data):
    """Validate user data with proper type checking"""
    
    if not isinstance(data, dict):
        raise TypeError("Data must be a dictionary")
    
    required_fields = {
        'name': str,
        'age': int,
        'email': str
    }
    
    for field, expected_type in required_fields.items():
        if field not in data:
            raise ValueError(f"Missing required field: {field}")
        
        if not isinstance(data[field], expected_type):
            raise TypeError(f"Field '{field}' must be of type {expected_type.__name__}")
        
        if expected_type == str and not data[field].strip():
            raise ValueError(f"Field '{field}' cannot be empty")
        
        if expected_type == int and data[field] < 0:
            raise ValueError(f"Field '{field}' must be non-negative")
    
    return True

# Test validation
valid_data = {"name": "Alice", "age": 25, "email": "alice@example.com"}
invalid_data = {"name": "", "age": -5, "email": 123}

try:
    validate_user_data(valid_data)
    print("Valid data passed validation")
except (TypeError, ValueError) as e:
    print(f"Validation error: {e}")

try:
    validate_user_data(invalid_data)
except (TypeError, ValueError) as e:
    print(f"Invalid data failed validation: {e}")
```

```javascript !! js
// JavaScript type checking

function comprehensiveTypeCheck(value) {
    // Basic type checking
    console.log(`Value: ${JSON.stringify(value)}`);
    console.log(`Type: ${typeof value}`);
    console.log(`Constructor: ${value?.constructor?.name || 'N/A'}`);
    
    // Specific type checks
    console.log(`Is number: ${typeof value === 'number'}`);
    console.log(`Is string: ${typeof value === 'string'}`);
    console.log(`Is boolean: ${typeof value === 'boolean'}`);
    console.log(`Is object: ${typeof value === 'object'}`);
    console.log(`Is function: ${typeof value === 'function'}`);
    
    // More specific object type checks
    console.log(`Is null: ${value === null}`);
    console.log(`Is undefined: ${value === undefined}`);
    console.log(`Is array: ${Array.isArray(value)}`);
    console.log(`Is date: ${value instanceof Date}`);
    console.log(`Is regex: ${value instanceof RegExp}`);
    
    // Duck typing checks
    console.log(`Has length: ${value && typeof value.length === 'number'}`);
    console.log(`Has keys method: ${value && typeof value.keys === 'function'}`);
    console.log(`Has push method: ${value && typeof value.push === 'function'}`);
    console.log(`Is iterable: ${value && typeof value[Symbol.iterator] === 'function'}`);
    
    console.log("-".repeat(40));
}

// Test different types
const testValues = [
    42,
    "hello",
    [1, 2, 3],
    {key: "value"},
    null,
    undefined,
    function() {},
    new Date(),
    /regex/
];

testValues.forEach(val => {
    comprehensiveTypeCheck(val);
});

// Type validation function
function validateUserData(data) {
    // Check if data is an object
    if (typeof data !== 'object' || data === null || Array.isArray(data)) {
        throw new TypeError("Data must be a plain object");
    }
    
    const requiredFields = {
        name: 'string',
        age: 'number',
        email: 'string'
    };
    
    for (const [field, expectedType] of Object.entries(requiredFields)) {
        if (!(field in data)) {
            throw new Error(`Missing required field: ${field}`);
        }
        
        if (typeof data[field] !== expectedType) {
            throw new TypeError(`Field '${field}' must be of type ${expectedType}`);
        }
        
        if (expectedType === 'string' && !data[field].trim()) {
            throw new Error(`Field '${field}' cannot be empty`);
        }
        
        if (expectedType === 'number' && (data[field] < 0 || !Number.isFinite(data[field]))) {
            throw new Error(`Field '${field}' must be a non-negative finite number`);
        }
    }
    
    return true;
}

// Advanced type checking utilities
class TypeChecker {
    static isInteger(value) {
        return Number.isInteger(value);
    }
    
    static isFloat(value) {
        return typeof value === 'number' && !Number.isInteger(value);
    }
    
    static isEmptyObject(value) {
        return typeof value === 'object' && 
               value !== null && 
               !Array.isArray(value) && 
               Object.keys(value).length === 0;
    }
    
    static isEmptyArray(value) {
        return Array.isArray(value) && value.length === 0;
    }
    
    static isPrimitive(value) {
        return value !== Object(value);
    }
}

// Test validation
const validData = {name: "Alice", age: 25, email: "alice@example.com"};
const invalidData = {name: "", age: -5, email: 123};

try {
    validateUserData(validData);
    console.log("Valid data passed validation");
} catch (error) {
    console.log(`Validation error: ${error.message}`);
}

try {
    validateUserData(invalidData);
} catch (error) {
    console.log(`Invalid data failed validation: ${error.message}`);
}

// Test utility functions
console.log(`Is 42 integer: ${TypeChecker.isInteger(42)}`);
console.log(`Is 3.14 float: ${TypeChecker.isFloat(3.14)}`);
console.log(`Is {} empty object: ${TypeChecker.isEmptyObject({})}`);
console.log(`Is [] empty array: ${TypeChecker.isEmptyArray([])}`);
console.log(`Is "hello" primitive: ${TypeChecker.isPrimitive("hello")}`);
```
</PythonEditor>

## Null and Undefined Handling

### None vs Null vs Undefined

<PythonEditor title="Null Value Handling" compare={true}>
```python !! py
# Python: Only None for null values

def analyze_none_handling():
    # Python has only None
    value = None
    
    print(f"None value: {value}")
    print(f"None type: {type(value)}")
    print(f"None is None: {value is None}")
    print(f"None == None: {value == None}")  # Works but not recommended
    print(f"bool(None): {bool(None)}")
    
    # Default parameter handling
    def greet(name=None):
        if name is None:
            return "Hello, anonymous!"
        return f"Hello, {name}!"
    
    print(f"No argument: {greet()}")
    print(f"With argument: {greet('Alice')}")
    print(f"Explicit None: {greet(None)}")
    
    # Optional return values
    def find_user(user_id):
        users = {1: "Alice", 2: "Bob"}
        return users.get(user_id)  # Returns None if not found
    
    user = find_user(3)
    if user is None:
        print("User not found")
    else:
        print(f"Found user: {user}")
    
    # Dictionary with None values
    data = {
        "name": "Alice",
        "age": None,  # Explicitly set to None
        "city": "New York"
    }
    
    for key, value in data.items():
        if value is None:
            print(f"{key} is not set")
        else:
            print(f"{key}: {value}")

analyze_none_handling()

# None in collections
def process_values(values):
    """Process a list that might contain None values"""
    valid_values = [v for v in values if v is not None]
    none_count = len(values) - len(valid_values)
    
    print(f"Total values: {len(values)}")
    print(f"Valid values: {len(valid_values)}")
    print(f"None values: {none_count}")
    
    return valid_values

test_list = [1, None, 3, None, 5]
result = process_values(test_list)
print(f"Processed result: {result}")
```

```javascript !! js
// JavaScript: Both null and undefined

function analyzeNullUndefinedHandling() {
    // JavaScript has null and undefined
    const nullValue = null;
    let undefinedValue;  // undefined by default
    const explicitUndefined = undefined;
    
    console.log(`Null value: ${nullValue}`);
    console.log(`Undefined value: ${undefinedValue}`);
    console.log(`Null type: ${typeof nullValue}`);       // "object" (quirk)
    console.log(`Undefined type: ${typeof undefinedValue}`); // "undefined"
    
    // Equality comparisons
    console.log(`null === null: ${null === null}`);
    console.log(`undefined === undefined: ${undefined === undefined}`);
    console.log(`null === undefined: ${null === undefined}`);  // false
    console.log(`null == undefined: ${null == undefined}`);    // true (coerced)
    
    // Boolean conversion
    console.log(`Boolean(null): ${Boolean(null)}`);
    console.log(`Boolean(undefined): ${Boolean(undefined)}`);
    
    // Default parameter handling
    function greet(name = "anonymous") {
        // ES6 default only triggers for undefined, not null
        return `Hello, ${name}!`;
    }
    
    console.log(`No argument: ${greet()}`);
    console.log(`Undefined argument: ${greet(undefined)}`);
    console.log(`Null argument: ${greet(null)}`);  // "Hello, null!"
    
    // Better default handling
    function greetSafe(name) {
        const safeName = name ?? "anonymous";  // Nullish coalescing
        return `Hello, ${safeName}!`;
    }
    
    console.log(`Safe - no arg: ${greetSafe()}`);
    console.log(`Safe - undefined: ${greetSafe(undefined)}`);
    console.log(`Safe - null: ${greetSafe(null)}`);
    
    // Optional return values
    function findUser(userId) {
        const users = {1: "Alice", 2: "Bob"};
        return users[userId];  // Returns undefined if not found
    }
    
    const user = findUser(3);
    if (user === undefined) {
        console.log("User not found");
    } else {
        console.log(`Found user: ${user}`);
    }
    
    // Object with null/undefined values
    const data = {
        name: "Alice",
        age: null,        // Explicitly set to null
        city: "New York"
        // email is undefined (not present)
    };
    
    for (const [key, value] of Object.entries(data)) {
        if (value === null) {
            console.log(`${key} is explicitly null`);
        } else if (value === undefined) {
            console.log(`${key} is undefined`);
        } else {
            console.log(`${key}: ${value}`);
        }
    }
    
    // Check for property existence
    console.log(`Has name property: ${'name' in data}`);
    console.log(`Has email property: ${'email' in data}`);
    console.log(`Name value: ${data.name}`);
    console.log(`Email value: ${data.email}`);  // undefined
}

analyzeNullUndefinedHandling();

// Null/undefined in arrays
function processValues(values) {
    // Filter out null and undefined
    const validValues = values.filter(v => v != null);  // Filters both null and undefined
    const nullCount = values.filter(v => v === null).length;
    const undefinedCount = values.filter(v => v === undefined).length;
    
    console.log(`Total values: ${values.length}`);
    console.log(`Valid values: ${validValues.length}`);
    console.log(`Null values: ${nullCount}`);
    console.log(`Undefined values: ${undefinedCount}`);
    
    return validValues;
}

const testArray = [1, null, 3, undefined, 5];
const result = processValues(testArray);
console.log(`Processed result: ${result}`);

// Optional chaining (ES2020)
const user = {
    name: "Alice",
    address: {
        street: "123 Main St",
        city: "New York"
    }
};

const userWithoutAddress = {
    name: "Bob"
};

console.log(`Alice's city: ${user.address?.city}`);           // "New York"
console.log(`Bob's city: ${userWithoutAddress.address?.city}`); // undefined
console.log(`Nested access: ${user.address?.country?.code}`);   // undefined
```
</PythonEditor>

## Common Type-Related Pitfalls

### Avoiding Common Mistakes

<PythonEditor title="Common Pitfalls" compare={true}>
```python !! py
# Python type-related pitfalls and solutions

# Pitfall 1: Mutable default arguments
def bad_function(items=[]):  # BAD: Mutable default
    items.append("new item")
    return items

def good_function(items=None):  # GOOD: Use None
    if items is None:
        items = []
    items.append("new item")
    return items

# Test the difference
print("Bad function:")
print(bad_function())      # ['new item']
print(bad_function())      # ['new item', 'new item'] - Shared state!

print("\nGood function:")
print(good_function())     # ['new item']
print(good_function())     # ['new item'] - Fresh list each time

# Pitfall 2: Late binding closures
def create_functions_bad():
    functions = []
    for i in range(3):
        functions.append(lambda: i)  # BAD: Late binding
    return functions

def create_functions_good():
    functions = []
    for i in range(3):
        functions.append(lambda x=i: x)  # GOOD: Default argument
    return functions

print("\nLate binding issue:")
bad_funcs = create_functions_bad()
for func in bad_funcs:
    print(func())  # All print 2!

print("\nFixed late binding:")
good_funcs = create_functions_good()
for func in good_funcs:
    print(func())  # Prints 0, 1, 2

# Pitfall 3: Integer division changes in Python 3
def division_examples():
    # Python 3 behavior
    print(f"7 / 2 = {7 / 2}")      # 3.5 (true division)
    print(f"7 // 2 = {7 // 2}")    # 3 (floor division)
    
    # Be explicit about integer division
    def safe_divide(a, b):
        if b == 0:
            raise ValueError("Cannot divide by zero")
        return a / b
    
    try:
        result = safe_divide(7, 2)
        print(f"Safe division: {result}")
    except ValueError as e:
        print(f"Division error: {e}")

division_examples()

# Pitfall 4: String formatting type issues
def formatting_issues():
    # Be careful with None values in formatting
    name = None
    age = 25
    
    # This works but might not be what you want
    print(f"Name: {name}, Age: {age}")  # "Name: None, Age: 25"
    
    # Better approach
    safe_name = name or "Unknown"
    print(f"Name: {safe_name}, Age: {age}")  # "Name: Unknown, Age: 25"

formatting_issues()
```

```javascript !! js
// JavaScript type-related pitfalls and solutions

// Pitfall 1: Type coercion in comparisons
function comparisonPitfalls() {
    console.log("Comparison pitfalls:");
    
    // Surprising equality results
    console.log(`"" == false: ${"" == false}`);        // true
    console.log(`0 == false: ${0 == false}`);          // true
    console.log(`[] == false: ${[] == false}`);        // true
    console.log(`"0" == false: ${"0" == false}`);      // true
    console.log(`null == undefined: ${null == undefined}`); // true
    
    // Always use strict equality
    console.log(`"" === false: ${"" === false}`);      // false
    console.log(`0 === false: ${0 === false}`);        // false
    console.log(`[] === false: ${[] === false}`);      // false
    console.log(`null === undefined: ${null === undefined}`); // false
}

comparisonPitfalls();

// Pitfall 2: Array detection
function arrayDetection() {
    console.log("\nArray detection pitfalls:");
    
    const arr = [1, 2, 3];
    
    // Wrong ways to check for arrays
    console.log(`typeof []: ${typeof arr}`);           // "object" - not helpful
    console.log(`arr instanceof Array: ${arr instanceof Array}`); // true, but can fail with frames
    
    // Correct way
    console.log(`Array.isArray([]): ${Array.isArray(arr)}`); // true - reliable
}

arrayDetection();

// Pitfall 3: Floating point precision
function floatingPointIssues() {
    console.log("\nFloating point pitfalls:");
    
    const result = 0.1 + 0.2;
    console.log(`0.1 + 0.2 = ${result}`);              // 0.30000000000000004
    console.log(`0.1 + 0.2 === 0.3: ${result === 0.3}`); // false
    
    // Solution: Use epsilon comparison
    function isEqual(a, b, epsilon = Number.EPSILON) {
        return Math.abs(a - b) < epsilon;
    }
    
    console.log(`Is equal with epsilon: ${isEqual(result, 0.3)}`); // true
    
    // Or for currency, work in cents
    const price1 = 10; // 10 cents
    const price2 = 20; // 20 cents
    const total = price1 + price2; // 30 cents
    console.log(`Price total: $${total / 100}`); // $0.30
}

floatingPointIssues();

// Pitfall 4: NaN behavior
function nanPitfalls() {
    console.log("\nNaN pitfalls:");
    
    const notANumber = Number("abc");
    console.log(`Number("abc"): ${notANumber}`);        // NaN
    console.log(`NaN === NaN: ${NaN === NaN}`);         // false!
    console.log(`notANumber === notANumber: ${notANumber === notANumber}`); // false!
    
    // Correct way to check for NaN
    console.log(`isNaN(notANumber): ${isNaN(notANumber)}`);     // true
    console.log(`Number.isNaN(notANumber): ${Number.isNaN(notANumber)}`); // true (more reliable)
    
    // isNaN coerces, Number.isNaN doesn't
    console.log(`isNaN("abc"): ${isNaN("abc")}`);           // true (coerced)
    console.log(`Number.isNaN("abc"): ${Number.isNaN("abc")}`); // false (no coercion)
}

nanPitfalls();

// Pitfall 5: Hoisting and temporal dead zone
function hoistingIssues() {
    console.log("\nHoisting pitfalls:");
    
    // var hoisting (avoid this)
    console.log(`varVariable before declaration: ${typeof varVariable}`); // "undefined"
    var varVariable = "I'm hoisted";
    
    // let/const temporal dead zone
    try {
        console.log(letVariable); // ReferenceError
        let letVariable = "I'm not hoisted";
    } catch (error) {
        console.log(`Let error: ${error.message}`);
    }
    
    // Function hoisting
    console.log(`Hoisted function: ${hoistedFunction()}`); // Works
    
    function hoistedFunction() {
        return "I'm hoisted too";
    }
    
    // Function expression not hoisted
    try {
        console.log(notHoisted()); // TypeError
    } catch (error) {
        console.log(`Function expression error: ${error.message}`);
    }
    
    const notHoisted = function() {
        return "I'm not hoisted";
    };
}

hoistingIssues();

// Pitfall 6: Object property access
function propertyAccessPitfalls() {
    console.log("\nProperty access pitfalls:");
    
    const obj = {
        "normal-key": "value1",
        "123": "value2",
        "": "value3"
    };
    
    // Dot notation limitations
    // console.log(obj.normal-key);    // SyntaxError
    // console.log(obj.123);           // SyntaxError
    
    // Use bracket notation for special keys
    console.log(`obj["normal-key"]: ${obj["normal-key"]}`);
    console.log(`obj["123"]: ${obj["123"]}`);
    console.log(`obj[""]: ${obj[""]}`);
    
    // Safe property access
    const userData = {name: "Alice"};
    console.log(`Safe access: ${userData.age?.toString() || "Not provided"}`);
    
    // Using optional chaining and nullish coalescing
    const config = {
        database: {
            host: "localhost"
            // port is missing
        }
    };
    
    const port = config.database?.port ?? 5432;
    console.log(`Database port: ${port}`);
}

propertyAccessPitfalls();
```
</PythonEditor>

## Best Practices for Type Safety

### Writing Type-Safe Code

<PythonEditor title="Type Safety Best Practices" compare={true}>
```python !! py
# Python type safety best practices

from typing import Optional, Union, List, Dict, Any
from dataclasses import dataclass

# Use type hints for better code documentation
def calculate_total(prices: List[float], tax_rate: float = 0.1) -> float:
    """Calculate total price including tax."""
    if not prices:
        return 0.0
    
    subtotal = sum(prices)
    return subtotal * (1 + tax_rate)

# Use dataclasses for structured data
@dataclass
class User:
    name: str
    age: int
    email: Optional[str] = None
    
    def __post_init__(self):
        if self.age < 0:
            raise ValueError("Age cannot be negative")
        if not self.name.strip():
            raise ValueError("Name cannot be empty")

# Input validation with type checking
def process_user_input(data: Dict[str, Any]) -> User:
    """Process and validate user input."""
    
    # Validate required fields
    required_fields = ['name', 'age']
    for field in required_fields:
        if field not in data:
            raise ValueError(f"Missing required field: {field}")
    
    # Type validation
    if not isinstance(data['name'], str):
        raise TypeError("Name must be a string")
    
    if not isinstance(data['age'], int):
        raise TypeError("Age must be an integer")
    
    # Optional field validation
    email = data.get('email')
    if email is not None and not isinstance(email, str):
        raise TypeError("Email must be a string")
    
    return User(
        name=data['name'],
        age=data['age'],
        email=email
    )

# Safe type conversion
def safe_int_conversion(value: Any) -> Optional[int]:
    """Safely convert value to integer."""
    if isinstance(value, int):
        return value
    
    if isinstance(value, str):
        try:
            return int(value)
        except ValueError:
            return None
    
    if isinstance(value, float):
        if value.is_integer():
            return int(value)
        return None
    
    return None

# Test the functions
try:
    user_data = {"name": "Alice", "age": 25, "email": "alice@example.com"}
    user = process_user_input(user_data)
    print(f"Created user: {user}")
    
    prices = [10.99, 5.50, 3.25]
    total = calculate_total(prices)
    print(f"Total price: ${total:.2f}")
    
    # Test safe conversion
    test_values = ["123", 45.0, "abc", 3.14]
    for val in test_values:
        converted = safe_int_conversion(val)
        print(f"{val} -> {converted}")
        
except (ValueError, TypeError) as e:
    print(f"Error: {e}")
```

```javascript !! js
// JavaScript type safety best practices

// Use TypeScript-style JSDoc comments for type documentation
/**
 * Calculate total price including tax
 * @param {number[]} prices - Array of prices
 * @param {number} taxRate - Tax rate (default 0.1)
 * @returns {number} Total price with tax
 */
function calculateTotal(prices, taxRate = 0.1) {
    // Input validation
    if (!Array.isArray(prices)) {
        throw new TypeError("Prices must be an array");
    }
    
    if (typeof taxRate !== 'number' || taxRate < 0) {
        throw new TypeError("Tax rate must be a non-negative number");
    }
    
    if (prices.length === 0) {
        return 0;
    }
    
    // Validate all prices are numbers
    for (const price of prices) {
        if (typeof price !== 'number' || !Number.isFinite(price)) {
            throw new TypeError("All prices must be finite numbers");
        }
    }
    
    const subtotal = prices.reduce((sum, price) => sum + price, 0);
    return subtotal * (1 + taxRate);
}

// Use classes for structured data with validation
class User {
    /**
     * @param {string} name - User name
     * @param {number} age - User age
     * @param {string|null} email - User email (optional)
     */
    constructor(name, age, email = null) {
        this.setName(name);
        this.setAge(age);
        this.setEmail(email);
    }
    
    setName(name) {
        if (typeof name !== 'string') {
            throw new TypeError("Name must be a string");
        }
        if (!name.trim()) {
            throw new Error("Name cannot be empty");
        }
        this.name = name;
    }
    
    setAge(age) {
        if (!Number.isInteger(age)) {
            throw new TypeError("Age must be an integer");
        }
        if (age < 0) {
            throw new Error("Age cannot be negative");
        }
        this.age = age;
    }
    
    setEmail(email) {
        if (email !== null && typeof email !== 'string') {
            throw new TypeError("Email must be a string or null");
        }
        this.email = email;
    }
    
    toJSON() {
        return {
            name: this.name,
            age: this.age,
            email: this.email
        };
    }
}

// Input validation utility
class InputValidator {
    static validateUserData(data) {
        if (typeof data !== 'object' || data === null || Array.isArray(data)) {
            throw new TypeError("Data must be a plain object");
        }
        
        const requiredFields = ['name', 'age'];
        for (const field of requiredFields) {
            if (!(field in data)) {
                throw new Error(`Missing required field: ${field}`);
            }
        }
        
        return new User(data.name, data.age, data.email || null);
    }
    
    static isValidEmail(email) {
        if (typeof email !== 'string') return false;
        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        return emailRegex.test(email);
    }
}

// Safe type conversion utilities
class TypeConverter {
    static toSafeInteger(value) {
        if (Number.isInteger(value)) {
            return value;
        }
        
        if (typeof value === 'string') {
            const parsed = parseInt(value, 10);
            return Number.isNaN(parsed) ? null : parsed;
        }
        
        if (typeof value === 'number' && Number.isFinite(value)) {
            return Number.isInteger(value) ? value : Math.floor(value);
        }
        
        return null;
    }
    
    static toSafeFloat(value) {
        if (typeof value === 'number' && Number.isFinite(value)) {
            return value;
        }
        
        if (typeof value === 'string') {
            const parsed = parseFloat(value);
            return Number.isNaN(parsed) ? null : parsed;
        }
        
        return null;
    }
    
    static toSafeString(value) {
        if (typeof value === 'string') {
            return value;
        }
        
        if (value === null || value === undefined) {
            return null;
        }
        
        return String(value);
    }
}

// Runtime type checking utility
class TypeChecker {
    static assertType(value, expectedType, fieldName = 'value') {
        const actualType = typeof value;
        if (actualType !== expectedType) {
            throw new TypeError(`${fieldName} must be ${expectedType}, got ${actualType}`);
        }
    }
    
    static assertArray(value, fieldName = 'value') {
        if (!Array.isArray(value)) {
            throw new TypeError(`${fieldName} must be an array`);
        }
    }
    
    static assertInstanceOf(value, constructor, fieldName = 'value') {
        if (!(value instanceof constructor)) {
            throw new TypeError(`${fieldName} must be an instance of ${constructor.name}`);
        }
    }
}

// Test the functions
try {
    const userData = {name: "Alice", age: 25, email: "alice@example.com"};
    const user = InputValidator.validateUserData(userData);
    console.log(`Created user: ${JSON.stringify(user)}`);
    
    const prices = [10.99, 5.50, 3.25];
    const total = calculateTotal(prices);
    console.log(`Total price: $${total.toFixed(2)}`);
    
    // Test safe conversion
    const testValues = ["123", 45.0, "abc", 3.14, null];
    testValues.forEach(val => {
        const converted = TypeConverter.toSafeInteger(val);
        console.log(`${JSON.stringify(val)} -> ${converted}`);
    });
    
    // Type assertions
    TypeChecker.assertType("hello", "string", "greeting");
    TypeChecker.assertArray([1, 2, 3], "numbers");
    console.log("All type checks passed");
    
} catch (error) {
    console.log(`Error: ${error.message}`);
}

// Modern JavaScript features for type safety
class ModernTypeSafety {
    // Use optional chaining and nullish coalescing
    static safePropertyAccess(obj, path) {
        // Safe nested property access
        return obj?.user?.profile?.name ?? 'Unknown';
    }
    
    // Use destructuring with defaults
    static processConfig({host = 'localhost', port = 3000, ssl = false} = {}) {
        return {host, port, ssl};
    }
    
    // Use Map for better type safety than objects
    static createTypedMap() {
        const userMap = new Map();
        userMap.set('alice', {name: 'Alice', age: 25});
        userMap.set('bob', {name: 'Bob', age: 30});
        return userMap;
    }
}

// Test modern features
const config = ModernTypeSafety.processConfig({host: 'example.com'});
console.log(`Config: ${JSON.stringify(config)}`);

const userMap = ModernTypeSafety.createTypedMap();
console.log(`User map size: ${userMap.size}`);
```
</PythonEditor>

## Summary

This module covered the crucial differences between Python and JavaScript's dynamic type systems:

### Key Takeaways:

1. **Type System Philosophy**: Python is strongly-typed (explicit conversions), JavaScript is weakly-typed (automatic coercion)
2. **Falsy Values**: Different rules - JavaScript has more falsy values including empty arrays being truthy
3. **Null Handling**: Python has only `None`, JavaScript has both `null` and `undefined`
4. **Type Checking**: Different approaches and tools available in each language
5. **Common Pitfalls**: Understanding type coercion, comparison operators, and type validation

### Best Practices:
- Always use strict equality (`===`) in JavaScript
- Validate inputs explicitly in both languages
- Use type hints in Python and consider TypeScript for JavaScript
- Be aware of automatic type conversions in JavaScript
- Use modern JavaScript features like optional chaining and nullish coalescing

Understanding these type system differences is crucial for writing robust code when transitioning from Python to JavaScript. The next module will explore function definition and scope mechanisms in detail.
